package cn.catsdad.auth.service;

import cn.catsdad.constant.iConstant.IAuthConstant;
import cn.catsdad.util.RedisUtil;
import cn.catsdad.util.StringUtil;
import cn.catsdad.util.uuid.UUID;
import cn.catsdad.vo.LoginUser;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.auth0.jwt.interfaces.Verification;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * token操作服务
 *
 * @author xuyonglong
 * @version V1.0 2023/3/23
 */
@Component
public class TokenService {

    @Value("${token.expireMins:30}")
    private int expireMins;

    @Value("${token.key}")
    private String key;

    @Value("${token.header}")
    private String header;

    /**
     * 创建token
     *
     * @param loginUser 登录用户
     * @return 生成的token
     */
    public String createToken(LoginUser loginUser) {
        String tokenUUID = UUID.fastUUID().toString();
        loginUser.setToken(tokenUUID);
        refreshToken(loginUser);
        Map<String, Object> claims = new HashMap<>();
        claims.put(IAuthConstant.Cache.TOKEN_KEY, tokenUUID);
        return createToken(claims);
    }

    /**
     * 刷寻token
     *
     * @param loginUser 登录用户
     */
    public void refreshToken(LoginUser loginUser) {
        //设置登录时间
        loginUser.setLoginTime(System.currentTimeMillis());
        long expireMillis = expireMins * 60L * 1000L;
        //更新过期时间
        loginUser.setExpireTime(loginUser.getLoginTime() + expireMillis);
        //缓存登录对象
        RedisUtil.setCacheObject(getTokenKey(loginUser.getToken()), loginUser, expireMins, TimeUnit.MINUTES);
    }

    /**
     * 根据请求获取用户
     *
     * @param request http请求
     * @return 用户
     */
    public LoginUser getLoginUser(HttpServletRequest request) {
        String token = getTokenFromRequest(request);
        if (StringUtil.isNotEmpty(token)) {
            String tokenUUID = parseToken(token);
            String userRedisKey = getTokenKey(tokenUUID);
            return RedisUtil.getCacheObject(userRedisKey);
        }
        return null;
    }

    /**
     * 每次请求验证token，如果在配置的过期时间内请求则刷新token
     *
     * @param loginUser 登录用户
     */
    public void verifyTokenForPerRequest(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        long expireMillis = expireMins * 60 * 1000L;
        if (expireTime - currentTime <= expireMillis) {
            refreshToken(loginUser);
        }
    }

    /**
     * 删除缓存的用户
     *
     * @param tokenUUID token解析后的uuid
     */
    public void delCacheUser(String tokenUUID) {
        String tokenCacheKey = getTokenKey(tokenUUID);
        RedisUtil.deleteObject(tokenCacheKey);
    }

    /**
     * 从请求头获取token
     *
     * @param request 请求
     * @return token
     */
    private String getTokenFromRequest(HttpServletRequest request) {
        String token = request.getHeader(header);
        if (StringUtil.isNotEmpty(token) && token.startsWith(IAuthConstant.Token.TOKEN_PREFIX)) {
            token = token.replace(IAuthConstant.Token.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 解析token
     *
     * @param token 请求头中携带的token
     * @return 完成解析的token uuid
     */
    private String parseToken(String token) {
        Algorithm algorithm = Algorithm.HMAC256(key);

        Verification verification = JWT.require(algorithm);
        JWTVerifier jwtVerifier = verification.build();

        DecodedJWT decodedJWT = jwtVerifier.verify(token);
        Claim claim = decodedJWT.getClaim(IAuthConstant.Cache.TOKEN_KEY);
        return claim.asString();
    }

    /**
     * 生成token
     *
     * @param claims 生成的参数
     * @return token
     */
    private String createToken(Map<String, Object> claims) {
        Algorithm algorithm = Algorithm.HMAC256(key);
        JWTCreator.Builder jwt = JWT.create();
        // jwt.withHeader(header)
        // JWT 的 header 部分,该 map 可以是空的,因为有默认值{"alg":HS256,"typ":"JWT"}
        return IAuthConstant.Token.TOKEN_PREFIX + jwt.withHeader(new HashMap<>())
                .withClaim(IAuthConstant.Cache.TOKEN_KEY, claims.get(IAuthConstant.Cache.TOKEN_KEY).toString())
                .sign(algorithm);

    }

    /**
     * 获取token在redis中的key
     *
     * @param uuid token uuid
     * @return key
     */
    private String getTokenKey(String uuid) {
        return IAuthConstant.Cache.TOKEN_KEY + uuid;
    }
}
